package pl.edu.agh.uddiProxy.server;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.springframework.security.context.SecurityContextHolder;

import pl.edu.agh.uddiProxy.InquiryClient;
import pl.edu.agh.uddiProxy.client.Parameter;
import pl.edu.agh.uddiProxy.client.SearchService;
import pl.edu.agh.uddiProxy.client.StaticQueryPanel;
import pl.edu.agh.uddiProxy.dao.DAOHelper;
import pl.edu.agh.uddiProxy.dao.DynamicParameterDAO;
import pl.edu.agh.uddiProxy.types.ParameterType;
import pl.edu.agh.uddiProxy.types.ParametersMap;
import pl.edu.agh.uddiProxy.types.Permission;
import pl.edu.agh.uddiProxy.types.ServiceDynamicParameters;
import pl.edu.agh.uddiProxy.types.TModel;
import pl.edu.agh.uddiProxy.types.User;
import pl.edu.agh.uddiProxy.types.ValueType;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import dk.itst.uddi.client.query.FindBindingResult;
import dk.itst.uddi.client.query.FindBusinessResult;
import dk.itst.uddi.client.query.FindServiceQuery;
import dk.itst.uddi.client.query.FindServiceResult;
import dk.itst.uddi.client.types.core.BindingTemplate;
import dk.itst.uddi.client.types.core.BusinessInfo;
import dk.itst.uddi.client.types.core.ServiceInfo;
import dk.itst.uddi.client.types.core.TModelInfo;
import dk.itst.uddi.client.types.core.TModelInstanceInfo;

public class SearchServiceImpl extends RemoteServiceServlet implements SearchService {

	private static final int ACCURACY = 3;
	private static final String CATEGORY_ISO3166 = "uddi:uddi.org:ubr:categorization:iso3166";
	private static final String GENERAL_KEYWORDS = "uddi:uddi.org:categorization:general_keywords";
	private static final long serialVersionUID = -5808289517942745095L;
	private static CacheThread cacheThread = CacheThread.getInstance();

	Logger logger = Logger.getLogger(SearchServiceImpl.class);
	
	public Map<String, Parameter> getParametersMap() {
		Map<String, Parameter> map = new HashMap<String, Parameter>();
		
		ParametersMap parametersMap = DAOHelper.getParametersMap();
		for (String s : parametersMap.getParametersMap().keySet()) {
			ParameterType type = parametersMap.getParametersMap().get(s);
			Parameter parameter = new Parameter();
			parameter.setMaxValue(type.getMaxValue());
			parameter.setMinValue(type.getMinValue());
			parameter.setName(type.getName());
			parameter.setType(type.getType().toString());
			parameter.setUnit(type.getUnit());
			parameter.setEveryone(!type.getPermission().equals(Permission.EVERYONE));
			parameter.setOwner(!type.getPermission().equals(Permission.OWNER));
			map.put(s, parameter);
		}
		
		return map;
	}
	
	public List<Map<String, String>> search(String query, Map<String, String> staticMap, int limit) {
		if ((query == null || query.length() == 0) && (staticMap == null || staticMap.size() == 0)) {
			return null;
		}
		
		boolean cache = false;
		
		List<Map<String, String>> result = null;
		if (cache) {
			result = cacheThread.get(query, staticMap);
		}
		if (result == null) {
			
			InquiryClient inquiryClient = DAOHelper.getUDDIClient().getInquiryClient();
			Set<String> tModelSet = new HashSet<String>();
			
			if (staticMap.size() > 0) {
				int i = 0;
				for (String key : staticMap.keySet()) {
					if (key.equals(StaticQueryPanel.TMODEL_KEY)) {
						if (i == 0) {
							tModelSet.add(staticMap.get(key));
						} else {
							if (tModelSet.contains(staticMap.get(key))) {
								tModelSet.clear();
								tModelSet.add(staticMap.get(key));
							} else {
								tModelSet.clear();
							}
						}
					} else if (key.equals(StaticQueryPanel.SERVICE_NAME)) {
						FindServiceResult findServiceResult = inquiryClient.findService().addName(staticMap.get(key)).addQualifier(FindServiceQuery.APPROXIMATE_MATCH).execute();
						for (ServiceInfo serviceInfo : findServiceResult.getServiceInfos()) {
							FindBindingResult findBindingResult = inquiryClient.findBinding().setServiceKey(serviceInfo.getServiceKey()).execute();
							for (BindingTemplate bindingTemplate : findBindingResult.getTemplates()) {
								if (bindingTemplate.getTModelInstanceDetails() != null) {
									Set<String> tmpSet = new HashSet<String>();
									for (TModelInstanceInfo instanceInfo : bindingTemplate.getTModelInstanceDetails().getTModelInstanceInfoArray()) {
										tmpSet.add(instanceInfo.getTModelKey());
									}
									if (i == 0) {
										tModelSet.addAll(tmpSet);
									} else {
										tModelSet.retainAll(tmpSet);
									}
								}
							}
						}
					} else if (key.equals(StaticQueryPanel.PROVIDER_NAME)) {
						FindBusinessResult findBusinessResult = inquiryClient.findBusiness().addName(staticMap.get(key)).addQualifier(FindServiceQuery.APPROXIMATE_MATCH).execute();
						for (BusinessInfo businessInfo : findBusinessResult.getBusinessInfos()) {
							if (businessInfo.getServiceInfos() != null) {
								for (ServiceInfo serviceInfo : businessInfo.getServiceInfos().getServiceInfoArray()) {
									FindBindingResult findBindingResult = inquiryClient.findBinding().setServiceKey(serviceInfo.getServiceKey()).execute();
									for (BindingTemplate bindingTemplate : findBindingResult.getTemplates()) {
										if (bindingTemplate.getTModelInstanceDetails() != null) {
											Set<String> tmpSet = new HashSet<String>();
											for (TModelInstanceInfo instanceInfo : bindingTemplate.getTModelInstanceDetails().getTModelInstanceInfoArray()) {
												tmpSet.add(instanceInfo.getTModelKey());
											}
											if (i == 0) {
												tModelSet.addAll(tmpSet);
											} else {
												tModelSet.retainAll(tmpSet);
											}
										}
									}
								}
							}
						}
					} else if (key.equals(StaticQueryPanel.CATEGORY_ISO3166)) {
						Set<String> tmpSet = new HashSet<String>();
						for (TModelInfo info : inquiryClient.findTModel().addCategory(null, staticMap.get(key), CATEGORY_ISO3166).execute()) {
							tmpSet.add(info.getTModelKey());
						}
						if (i == 0) {
							tModelSet.addAll(tmpSet);
						} else {
							tModelSet.retainAll(tmpSet);
						}
					} else if (key.equals(StaticQueryPanel.GENERAL_KEYWORDS)) {
						Set<String> tmpSet = new HashSet<String>();
						for (TModelInfo info : inquiryClient.findTModel().addCategory("type", staticMap.get(key), GENERAL_KEYWORDS).execute()) {
							tmpSet.add(info.getTModelKey());
						}
						if (i == 0) {
							tModelSet.addAll(tmpSet);
						} else {
							tModelSet.retainAll(tmpSet);
						}
					}
					
					i++;
				}
			}
			
			result = new ArrayList<Map<String,String>>();
			
			if (staticMap.size() > 0 && tModelSet.size() == 0) {
				return null;
			}
			DynamicParameterDAO parameterDAO = DAOHelper.getDynamicParameterDAO();
			Map<String, ParameterType> parameterMap = DAOHelper.getParametersMap().getParametersMap();
			List<ServiceDynamicParameters> list = parameterDAO.getDynamicParameters(query, new LinkedList<String>(tModelSet));
			for (ServiceDynamicParameters parameters : list) {
				Map<String, String> map = new HashMap<String, String>();
				for (String key : parameterMap.keySet()) {
					String value = parameters.getParameterValue(key).getValue();
					if (DAOHelper.getParametersMap().getParametersMap().get(key).getType().isNumber() && value != null && value.contains(".")) {
						value = value.substring(0, Math.min(value.indexOf(".") + ACCURACY, value.length()));
					}
					map.put(key, value);
				}
				map.put(SearchService.T_MODEL_KEY, parameters.getTModelKey());
				map.put(SearchService.OWNER_KEY, inquiryClient.getAuthorizedName(parameters.getTModelKey()));
				map.put(SearchService.DISTANCE_KEY, Double.toString(parameters.getDistanceValue()));
				
				if (!cache && result.size() == limit) { 
					break;
				}
				result.add(map);
			}
			
			if (cache) {
				cacheThread.add(query, staticMap, result);
			}
		}
		
		logger.info("result size " + result.size());
		
		if (result != null && result.size() > limit && limit > 0) {
			return result.subList(0, limit);
		} else {
			return result;
		}
	}
	
	public Map<String, String> getParameters(String tModelKey) {
		Map<String, String> map = new HashMap<String, String>();
		
		ServiceDynamicParameters parameters = DAOHelper.getDynamicParameterDAO().getByTModel(tModelKey);
		for (String key : DAOHelper.getParametersMap().getParametersMap().keySet()) {
			String value = parameters.getParameterValue(key).getValue();
			map.put(key, value);
		}
		
		return map;
	}

	public void saveParameters(String tModelKey, Map<String, String> map) {
		ServiceDynamicParameters parameters = DAOHelper.getDynamicParameterDAO().getByTModel(tModelKey);
		Map<String, ParameterType> parameterMap = DAOHelper.getParametersMap().getParametersMap();
		for (String key : map.keySet()) {
			Permission permission = parameterMap.get(key).getPermission();
			User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
			
			if ((permission.equals(Permission.EVERYONE) || (permission.equals(Permission.OWNER) && user.isOwner(tModelKey) )) 
					&& map.get(key) != null && map.get(key).length() > 0) {
				Object v;
				if (parameterMap.get(key).getType().equals(ValueType.DOUBLE)) {
					v = Double.parseDouble(map.get(key));
				} else if (parameterMap.get(key).getType().equals(ValueType.INTEGER)) {
					v = Integer.parseInt(map.get(key));
				} else if (parameterMap.get(key).getType().equals(ValueType.LONG)) {
					v = Long.parseLong(map.get(key));
				} else  {
					v = map.get(key);
				}
				parameterMap.get(key).getCounter().count(parameterMap.get(key), parameters.getParameterValue(key), v);
			}
		}
		DAOHelper.getDynamicParameterDAO().update(parameters);
	}

	public String getTModelInfo(String tModelKey) {
		TModel model = DAOHelper.getUDDIClient().getInquiryClient().getTModel(tModelKey);
		
		return model.getModel().xmlText();
	}

	public void downloadWSDL(String tModelKey) {
		DAOHelper.getUDDIClient().getInquiryClient().getWSDL(tModelKey);
	}
	
}
